﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Mime;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using System.Xml.Linq;
using FiscalNET;

namespace TCPInvoice
{
    public class Invoice : DriverFiscal
    {
        #region Constants
        private const int KReadA = 65;
        private const int KReadB = 66;
        #endregion
        #region Properties
        // Incoming data from the client.
        public string DataRCV { get; set; }
        public string DataSND { get; set; }
        public Socket Listener { get; set; }
        public Socket Handler { get; set; }
        public string ComPort { get; set; }
        public int ComSpeed { get; set; }
        public int TCPPort { get; set; }
        public TextBox logTextBox { get; set; }
        #endregion

        /// <summary>
        /// Al iniciarse se busca un archivo de configuración con formato XML en la siguiente ruta: c:\TCPInvoice\config.xml
        /// el mismo deberá tendrá un nodo config y dentro de él los nodos "comPort" y "comSpeed" y "tcpPort" con los valores configurados dentro de ellos.
        /// Si no encuentra el archivo de configuración establecerá por default los siguientes valores: 
        /// comPort: COM1,comSpeed: 9600, TCPPort: 7000
        /// </summary>
        public Invoice(TextBox logTextBox)
        {
            this.logTextBox = logTextBox;
            Printer = "TM2000";
            try
            {
                const string xmlPath = "c:\\TCPInvoice";
                XDocument xdoc = XDocument.Load(xmlPath + "\\config.xml");
                ComPort = xdoc.Descendants("config").Select(item =>
                {
                    XElement xElement = item.Element("comPort");
                    return xElement != null ? xElement.Value : null;
                }).Single();
                ComSpeed = Convert.ToInt32(xdoc.Descendants("config").Select(item =>
                {
                    XElement element = item.Element("comSpeed");
                    return element != null ? element.Value : null;
                }).Single());
                TCPPort = Convert.ToInt32(xdoc.Descendants("config").Select(item =>
                {
                    XElement xElement1 = item.Element("TCPPort");
                    return xElement1 != null ? xElement1.Value : null;
                }).Single());
            }
            catch (Exception)
            {
                ComPort = "COM1";
                ComSpeed = 9600;
                TCPPort = 7000;
            }
        }

        public void WriteCommand(string command)
        {
            DataSND = (IF_WRITE(command)).ToString();
        }

        private void ReadFCA()
        {
            IF_WRITE("@ESTADO|A");
            var lastInvoice = IF_READ(6);
            var pos = GetPosNumber();
            DataSND = pos + lastInvoice;
        }

        private void ReadFCB()
        {
            IF_WRITE("@ESTADO|A");
            var lastInvoice = IF_READ(4);
            var pos = GetPosNumber();
            DataSND = pos + lastInvoice;
        }

        private string GetPosNumber()
        {
            IF_WRITE("@ESTADO|C");
            var pos = IF_READ(4);
            return pos;
        }

        public int OpenConnection()
        {
            return IF_OPEN(ComPort, ComSpeed);
        }

        public int CloseConnection()
        {
            return IF_CLOSE();
        }

        public void StartListening()
        {
            try
            {
                var localEndPoint = InicializeSocket();

                // Bind the socket to the local endpoint and 
                // listen for incoming connections.
                Listener.Bind(localEndPoint);
                Listener.Listen(10);

                // Start listening for connections.
                while (true)
                {
                    logTextBox.Invoke(
                        new MethodInvoker(
                            () =>
                            logTextBox.AppendText(@"Esperando conexión..." + Environment.NewLine +
                                                  Environment.NewLine)));
                    Handler = Listener.Accept();

                    DataSND = String.Empty;
                    // Program is suspended while waiting for an incoming connection.
                    DataRCV = null;
                    // An incoming connection needs to be processed.
                    //while (true)
                    // {
                    byte[] bytes = new byte[1024];
                    int bytesRec = Handler.Receive(bytes);
                    DataRCV += Encoding.ASCII.GetString(bytes, 0, bytesRec);
                    var commands = DataRCV.Split(new string[] { "<EOF>" }, StringSplitOptions.RemoveEmptyEntries);
                    int i = 0;
                    while (i < commands.Length && DataSND != "-1")
                    {
                        ProcessCommand(commands[i]);
                        i++;
                    }
                    if (!String.IsNullOrEmpty(DataSND))
                    {
                        // Echo the data back to the client.
                        byte[] msg = Encoding.ASCII.GetBytes(DataSND);
                        Handler.Send(msg);
                    }
                    //}

                    Handler.Shutdown(SocketShutdown.Both);
                    Handler.Close();
                }
            }
            catch (Exception ex)
            {
                logTextBox.Invoke(new MethodInvoker(() => logTextBox.AppendText(ex + Environment.NewLine)));
            }
        }

        protected IPEndPoint InicializeSocket()
        {
            // Establish the local endpoint for the socket.
            // Dns.GetHostName returns the name of the 
            // host running the application.
            //IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
            IPAddress ipAddress = IPAddress.Parse(LocalIPAddress());
            //IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
            IPEndPoint localEndPoint = new IPEndPoint(ipAddress, TCPPort);

            // Create a TCP/IP socket.

            Listener = new Socket(AddressFamily.InterNetwork,
                                  SocketType.Stream, ProtocolType.Tcp);
            return localEndPoint;
        }

        private void ProcessCommand(string comm)
        {
            try
            {
                logTextBox.Invoke(new MethodInvoker(() => logTextBox.AppendText(DateTime.Now.ToShortTimeString() + ": " + comm + Environment.NewLine)));
                int command;
                if (int.TryParse(comm, out command))
                {
                    switch (command)
                    {
                        case KReadA:
                            ReadFCA();
                            break;
                        case KReadB:
                            ReadFCB();
                            break;
                    }
                }
                else
                {
                    WriteCommand(comm);
                }
                logTextBox.Invoke(new MethodInvoker(() => logTextBox.AppendText(DateTime.Now.ToShortTimeString() + ": " + DataSND + Environment.NewLine)));

            }
            catch (Exception ex)
            {
                logTextBox.Invoke(new MethodInvoker(() => logTextBox.AppendText(ex + Environment.NewLine)));
            }
        }



        private string LocalIPAddress()
        {
            string localIP = "";
            IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    localIP = ip.ToString();
                    break;
                }
            }
            return localIP;
        }

    }
}
